package com.fc.nacosdemo.bpoint.aop;

import com.fc.nacosdemo.bpoint.annotation.BPoint;
import com.fc.nacosdemo.bpoint.annotation.EBusinessOPT;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.*;

@Aspect
@Component
public class BPointAspect {
    private Logger log = LoggerFactory.getLogger(BPointAspect.class);
    @Pointcut("@annotation(com.fc.nacosdemo.bpoint.annotation.BPoint)")
    public void bpPointCut() {}

    public static void main(String[] args) {
//        String s = "#user.name";
//        System.out.println(s.indexOf("."));
//        System.out.println(s=s.substring(1,s.length()));
//        String[] split = s.split("\\.");
//        System.out.println(split);
//        List<String> list = new ArrayList<>();
//        list.add("111");
//        list.remove(0);
//        System.out.println(list.size());
    }


    @Before("bpPointCut()")
    public void thirdBindInfoModifyBefore(JoinPoint joinPoint) {
        // 将参数值设置到注解属性上
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        BPoint annotation = signature.getMethod().getAnnotation(BPoint.class);
        String[] valueNames = annotation.valueNames();
        EBusinessOPT key = annotation.key();

        if (valueNames == null || key == null) {
            return;
        }
        Object[] values = new Object[valueNames.length];


        // 获取方法参数名称
        String[] parameterNames = signature.getParameterNames();
        // 将方法参数名称跟对应的值 封装到 map
        Map<String, Object> paramMap = new HashMap<String, Object>();
        for (int i = 0; i < parameterNames.length; i++) {
            String parameterName = parameterNames[i];
            paramMap.put(parameterName, joinPoint.getArgs()[i]);
        }

        for (int i = 0; i < valueNames.length; i++) {
            String vname = valueNames[i];
            if (!vname.startsWith("#")) {
                values[i] = vname;
                continue;
            }
            vname = vname.substring(1, vname.length());
            if (vname.indexOf(".") == -1) {
                //未包含层级
                Object value = paramMap.get(vname);
                if (value == null)
                    log.error("BPoint 配置 出错 ！ , Method is {}, {} not find ! , 跟方法参数名称未匹配! ",
                            signature.getMethod().getClass().getName() + ":" + signature.getMethod().getName(), vname);
                values[i] = value;
                continue;
            }
            // 具有层级结构
            String[] names = vname.split("\\.");
            LinkedList<String> strings = new LinkedList<String>();
            for (String name : names) {
                strings.addLast(name);
            }
            strings.removeFirst();
            Object v = getV(paramMap.get(names[0]), strings);
            values[i] = v;
        }
        // 打印日志
        bp(key, values);

        /*if(StringUtils.isNotEmpty(fieldName = annotation.valueName())){
            String[] parameterNames = signature.getParameterNames();
            for (int i = 0; i < parameterNames.length; i++) {
                String parameterName = parameterNames[i];
                if (fieldName.equals(parameterName)) {

                }
            }
        }*/

        /*if(StringUtils.isNotEmpty(fieldName = annotation.key())){
            String[] parameterNames = signature.getParameterNames();
            for (int i = 0; i < parameterNames.length; i++) {
                String parameterName = parameterNames[i];
                if (fieldName.equals(parameterName)) {
                    InvocationHandler handler = Proxy.getInvocationHandler(annotation);
                    Map fieldValues = null;
                    try {
                        Field hField = handler.getClass().getDeclaredField("memberValues");
                        hField.setAccessible(true);
                        fieldValues = (Map) hField.get(handler);
                        fieldValues.put(fieldName, String.valueOf(joinPoint.getArgs()[i]));
                    } catch (NoSuchFieldException | IllegalAccessException e) {
                        if(fieldValues != null){
                            fieldValues.put(fieldName, "");
                        }
                    }
                }
            }
        }*/
    }

    // 递归获取层级参数值
    private Object getV(Object origin, LinkedList<String> names) {
        if(names.size() == 0){
            return origin;
        }
        String fieldName = names.getFirst();

        Class<?> aClass = origin.getClass();
        try {
            Field declaredField = aClass.getDeclaredField(fieldName);
            declaredField.setAccessible(true);
            origin = declaredField.get(origin);//name
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        names.removeFirst();
        return getV(origin, names);
    }


    @Around("bpPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        MethodSignature signature = (MethodSignature) point.getSignature();

        BPoint annotation = signature.getMethod().getAnnotation(BPoint.class);

        Annotation[][] parameterAnnotations =
                signature.getMethod().getParameterAnnotations();

        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
//        bp("point", "time","111");
        return result;
    }

    // 埋点日志打印
    private void bp(EBusinessOPT eBusinessOPT, Object... values) {
        String date = LocalDate.now().toString();
        String time = LocalTime.now().toString();
        String datetime = date + " " + time;
        List<String> logContent = eBusinessOPT.getLogContent();
        StringBuilder sb = new StringBuilder();
        for (String s2 : logContent) {
            sb.append("\"").append(s2).append("\"").append(":\"{}\",");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append("}");
//        log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
//                eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values);
        switch (values.length) {
            case 1:
                log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
                        eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0]);
                break;
            case 2:
                log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
                        eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1]);
                break;
            case 3:
                log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
                        eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2]);
                break;
            case 4:
                log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
                        eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2], values[3]);
                break;
            case 5:
                log.info("{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\"," + sb.toString(),
                        eBusinessOPT.getOptCode(), eBusinessOPT.getOptDesc(), datetime, values[0], values[1], values[2], values[3], values[4]);
                break;
        }
    }

    /*public static void main(String[] args) {
        List<String> logContent = Arrays.asList("userName","userAge","userPhone");
        StringBuilder sb = new StringBuilder();
        for (String s2 : logContent) {
            sb.append("\"").append(s2).append("\"").append(":\"{}\",");
        }
        sb.deleteCharAt(sb.length()-1);
        sb.append("}");
        String s = "{\"LOGLOG\":\"{}\",\"OPTLOG\":\"{}\",\"DATE-TIME\":\"{}\","+sb.toString();
        System.out.println(s);
        Map<String, String> map = JSON.parseObject(s,Map.class);
        System.out.println(map);
        Logger log = LoggerFactory.getLogger(BPointAspect.class);
        EBusinessOPT eBusinessOPT = EBusinessOPT.TEST;
        String date = LocalDate.now().toString();
        String time = LocalTime.now().toString();
        String datetime = date+" "+time;
        log.info("{\"BUSINESSLOG\":\"{}\",\"OPTLOG\":\"{}\",\"OPTTIME\":\"{}\","+sb.toString(),
                eBusinessOPT.getOptCode(),eBusinessOPT.getOptDesc(),datetime,"gaoyue","28","15545655565");
        log.info("{\"BUSINESSLOG\":\"{}\",\"OPTLOG\":\"{}\",\"OPTTIME\":\"{}\","+sb.toString(),
                eBusinessOPT.getOptCode(),eBusinessOPT.getOptDesc(),datetime,"gaoyue","28","15545655565");
    }*/

    /*public static void main(String[] args) {
        System.out.println(LocalDate.now().toString()+"----"+LocalTime.now().toString());
    }*/

}
